home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / admin / pareacct < prev    next >
Encoding:
Korn shell script  |  1997-08-26  |  7.2 KB  |  267 lines

  1. #!/bin/ksh
  2. # @(#) pareacct.ksh 1.1 97/06/29
  3. # 97/06/22 john h. dubois iii (john@armory.com)
  4. # 97/06/29 Added pax options.
  5. # todo: clean up core/temp/versioned files first;
  6. # todo: allow du -k as alternative to quota
  7.  
  8. # This utility requires version 1.7 or later of the 'stat' program,
  9. # available at ftp.armory.com.
  10.  
  11. ### start of doy-date lib
  12. # 97/06/29 john@armory.com
  13. typeset -i MStart
  14. function mstart_setup {
  15.     if [[ $(( `date +%y` % 4 )) = 0 ]]; then
  16.     set -A MStart 0 31 60 91 121 152 182 213 244 274 305 335 366
  17.     else
  18.     set -A MStart 0 31 59 90 120 151 181 212 243 273 304 334 365
  19.     fi
  20. }
  21.  
  22. function initMonths {
  23.     set -A mNum2Month "" jan feb mar apr may jun jul aug sep oct nov dec
  24. }
  25.  
  26. function month2num {
  27.     typeset -lL3 month=$1
  28.     typeset -i i=1
  29.     [ ${#mNum2Month} -eq 0 ] && initMonths
  30.     while [ i -le 12 ]; do
  31.     [ ${mNum2Month[i]} = "$month" ] && return $i
  32.     let i+=1
  33.     done
  34.     return 0
  35. }
  36.  
  37. # doy2date day-of-year
  38. # Returns the day-of-year converted to month & day (separated by a space)
  39. # in global doy2date
  40. # assumes conversion is for the current year
  41. function doy2date {
  42.     typeset -i M
  43.     M=$1/32+1
  44.     [ $1 -gt MStart[M] ] && M=M+1
  45.     doy2date="$M $(($1 - MStart[M-1]))"
  46. }
  47.  
  48. # date2doy month day-of-month
  49. # Returns the month & day-of-month converted to day-of-year in global date2doy
  50. # assumes conversion is for the current year
  51. function date2doy {
  52.     typeset -i month
  53.     [[ $1 = +([0-9]) ]] && month=$1 || {
  54.     month2num "$1" && return 0
  55.     month=$?
  56.     }
  57.     date2doy=$((MStart[month-1] + $2))
  58. }
  59.  
  60. # date2days year month day-of-month
  61. # Returns the number of complete days that passed from 1900 Jan 1 to the start
  62. # of the given date in global date2days.
  63. # The month may be given in numeric form (Jan=1) or by name, in which case at
  64. # least the first 3 characters must be passed (case is ignored).
  65. # Works from 1901 to 2099.
  66. # If year < 100, it is assumed to be part of the 1900 century
  67. typeset -i date2days
  68. function date2days {
  69.     typeset -i year=$1 day=$3 leap_days MDays month
  70.     [ year -ge 100 ] && let year-=1900
  71.     let leap_days=year/4+1
  72.     [[ $2 = +([0-9]) ]] && month=$2 || {
  73.     month2num "$2" && return -1
  74.     month=$?
  75.     }
  76.     [[ month -le 2 && $((year%4)) = 0 ]] && let leap_days-=1
  77.     [ ${#MDays[*]} -eq 0 ] &&
  78.     set -A MDays 0 0 31 59 90 120 151 181 212 243 273 304 334 365
  79.     date2days="year*365+MDays[month]+day-1+leap_days"
  80. }
  81.  
  82. # unixdays year month day-of-month
  83. # Returns the number of complete days that passed from 1970 Jan 1
  84. # to the start of the given date in global unixdays
  85. typeset -i unixdays
  86. function unixdays {
  87.     date2days "$@"
  88.     unixdays=date2days-25568
  89. }
  90.  
  91. # diffdays year1 month1 day-of-month1 year2 month2 day-of-month2 {
  92. # prints the number of complete days that passed from date 1 to date 2
  93. typeset -i diffdays
  94. function diffdays {
  95.     date2days $4 $5 $6
  96.     diffdays=date2days
  97.     date2days $1 $2 $3
  98.     diffdays=diffdays-date2days
  99. }
  100.  
  101. # Returns the UNIX epoch time in global curtime
  102. # Depends on SECONDS not being messed with
  103. # If all date utilities had %s this wouldn't be neccessary...
  104. typeset -i curtime _shell_start=-1
  105. function curtime {
  106.     typeset -i Y m d H M S
  107.     if [ _shell_start -eq -1 ]; then
  108.     TZ=0 date '+%Y %m %d %H %M %S' | read Y m d H M S
  109.     unixdays $Y $m $d
  110.     curtime="unixdays*86400+H*3600+M*60+S"
  111.     _shell_start=curtime-SECONDS
  112.     else
  113.     curtime=_shell_start+SECONDS
  114.     fi
  115. }
  116. ### end of doy-date lib
  117.  
  118. typeset -i daysold
  119. function daysold {
  120.     daysold="(curtime-$1)/86400"
  121. }
  122. ### start of main program
  123.  
  124. name=${0##*/}
  125. Usage=\
  126. "Usage: $name [-dhpr] [-a<min-age] [-s<min-size>] [-k<reduction>] user ..."
  127. typeset -i defMinSize=100 reduction=-1 i
  128. typeset -i minSize=defMinSize
  129. remove=false
  130. nodot=true
  131. unset amtime sd
  132. byProduct=false
  133. debug=false
  134. sort="sort -n"
  135.  
  136. while getopts a:hs:rk:dpx opt; do
  137.     case $opt in
  138.     h)
  139.     print -r -- \
  140. "$name: Reduce usage of users to their disk quota by removing large files,
  141. in order from oldest to newest.  In sorting the files by age, whichever of
  142. the access and modification time for each file is later (more recent) is used.
  143. By default, files are listed but not removed.
  144. $Usage
  145. Options:
  146. -d: Do not skip dotfiles (files whose name begins with '.').  By default, these
  147.     files are not considered candidates for removal.
  148. -k<reduction>: Set the reduction quota for all users to <reduction> KB.
  149.     By default, the reduction quota is set to the difference between a user's
  150.     usage and quota.
  151. -r: Remove files.
  152. -s<min-size>: Set the minimum size file to remove, in KB.
  153.     The default is ${defMinSize} KB.
  154. -a<min-age>: Set the minimum age a file to be removed, in days.  The default is
  155.     to remove files all the way to the current time if neccessary.
  156. -p: Remove files in order of age,filesize product.  For each file, the age
  157.     (as described above) and filesize are multiplied, and files are removed
  158.     in order of largest to smallest of this product.  
  159. -h: Print this help."
  160.     exit 0
  161.     ;;
  162.     k)
  163.     reduction=$OPTARG || exit 1
  164.     ;;
  165.     d)
  166.     nodot=false
  167.     ;;
  168.     s)
  169.     minSize=$OPTARG || exit 1
  170.     ;;
  171.     r)
  172.     remove=true
  173.     ;;
  174.     a)
  175.     i=$OPTARG || exit 1    # make sure it's an integer
  176.     # reduce i by 1 because -xtime +i means > i days
  177.     let i-=1
  178.     amtime="-atime +$i -mtime +$i"
  179.     ;;
  180.     p)
  181.     byProduct=true
  182.     typeset -i s_xtime s_size
  183.     # Make the age-size product be in days*KB, to reduce the chance that
  184.     # it will be > max_int
  185.     sort='
  186. while read s_xtime s_size s_fname; do
  187. print -r "$(((curtime-s_xtime)/86400*(s_size/1024))):$s_xtime $s_size $s_fname";
  188. done | sort -rn'
  189.     ;;
  190.     x)
  191.     debug=true
  192.     ;;
  193.     ?)
  194.     print -r -u2 "Use -h for help."
  195.     exit 1
  196.     ;;
  197.     esac
  198. done
  199.  
  200. # remove args that were options
  201. let OPTIND=OPTIND-1
  202. shift $OPTIND
  203.  
  204. if [ $# -lt 1 ]; then
  205.     print -r -u2 -- "$Usage
  206. Use -h for help."
  207.     exit 1
  208. fi
  209.  
  210. $remove && xargs rm -f 2>/dev/null |&
  211.  
  212. curtime    # sets $curtime
  213. typeset -i size usage quota over total num
  214. # Don't typeset -i xtime because it may be set to sortkey:time
  215. for user; do
  216.     cd ~$user || {
  217.     print -ru2 -- "Could not change working directory to ~$user"
  218.     continue
  219.     }
  220.     if [ reduction -ne -1 ]; then
  221.     over=reduction
  222.     else
  223.     quota -p $user | read u fs usage quota
  224.     if [ quota -le 0 -o usage -le 0 ]; then
  225.         print -ru2 -- \
  226. "Bad quota for user $user? -- \"$u $fs $usage $quota\""
  227.         continue
  228.     fi
  229.     over=usage-quota 
  230.     fi
  231.     total=0 num=0
  232.     if [ over -le 0 ]; then
  233.     print -ru2 "User $user is not over quota."
  234.     continue
  235.     fi
  236.     # Skip files with embedded whitespace, for simplicity.
  237.     find . -type f ! -name '*[
  238.      ]*' -size +$((minSize*2)) $amtime -print|stat -r -Lysn|eval $sort |
  239.     while read xtime size filename; do
  240.         $debug && $byProduct &&
  241.         print "size-age product of $filename: ${xtime%:*}"
  242.         $nodot && [[ "$filename" = */.+([!/]) ]] && continue
  243.         if $remove; then
  244.         $remove && print -p "$PWD/$filename"
  245.         print -r -- "Removing: $filename"
  246.         else
  247.         print -r -- "$filename"
  248.         fi
  249.         let total+=size/1024 num+=1
  250.         if [ total -ge over ]; then
  251.         print -ru2 -- \
  252. "Reduction quota of ${over} KB reached for user $user after $num file(s)."
  253.         break
  254.         fi
  255.     done
  256.     if [ total -lt over ]; then
  257.         print -ru2 -- \
  258. "Reduction quota of ${over} KB not reached for user $user after $num file(s)."
  259.         [ total -gt 0 ] && print -ru2 "Got only ${total} KB."
  260.     fi
  261.     if [ "$filename" != "" ]; then
  262.         daysold ${xtime#*:}
  263.         $byProduct && sd=" (size-age product: ${xtime%:*})"
  264.         print -ru2 -- "Most recent file is $daysold days old$sd: $filename"
  265.     fi
  266. done
  267.